-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: new BlockWriter implementation for block-as-file #355
base: main
Are you sure you want to change the base?
feat: new BlockWriter implementation for block-as-file #355
Conversation
Signed-off-by: Atanas Atanasov <[email protected]>
…l remain Signed-off-by: Atanas Atanasov <[email protected]>
1e81bdb
to
2d82213
Compare
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Intermediate thoughts at 9fa5c10:
|
Signed-off-by: Atanas Atanasov <[email protected]>
… clean up approach Signed-off-by: Atanas Atanasov <[email protected]>
Update at 5c0a707:
|
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few areas that could be improved.
common/src/main/java/com/hedera/block/common/utils/Preconditions.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/hedera/block/server/persistence/PersistenceInjectionModule.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/hedera/block/server/persistence/PersistenceInjectionModule.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/hedera/block/server/persistence/PersistenceInjectionModule.java
Outdated
Show resolved
Hide resolved
} catch (final Exception e) { | ||
// todo handle properly | ||
throw new RuntimeException(e); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless you have a very good reason, do not catch Exception
, ever (a vast array of bugs in real systems come from this seemingly innocuous block).
Instead, handle any specific exceptions you can, declare any checked exceptions necessary, and let callers handle things from there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The try-catch here would probably be greatly reworked or removed from here. This was done just to help develop the core functionality.
server/src/main/java/com/hedera/block/server/persistence/storage/write/BlockAsFileWriter.java
Outdated
Show resolved
Hide resolved
* @param blockPathResolver valid, {@code non-null} instance of {@link BlockPathResolver} | ||
* @return a new instance of {@link BlockAsFileWriterBuilder} | ||
*/ | ||
public static BlockAsFileWriterBuilder newBuilder( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Builder constructors should not, in general, take parameters. The whole point of a "builder" is to start empty and set each value as needed (chained by returning the builder from each set method) before a build
produces the result value.
What you've written here is an oddly structured factory, rather than a builder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree about the builders, in fact, I prefer to implement Joshua Bloch's builder where we have Builder
class that is internal to the class we want to build, since we can access directly the private filed of the internal builder from the target class and pass the builder itself as a constructor parameter.
I also agree that the builder should start "empty" and we set each value as needed.
I do not agree however that the builder constructor or static method that gets us the builder instance should have no arguments. There are some things that are mandatory for the given instance that will be constructed. Without forcing the caller to supply them, it is very error prone. The optional stuff should be settable later.
In our case it really looks like an odd factory, in fact I was thinking if we need the builders at all, we could just have static of()
or from()
or similar method directly inside the target class and omit the builders. The only place where we currently have an optional value is the folderPermissions
which I will remove the possibility to be set externally, they shall remain an internal property of the class. All other builders are in the same fashion.
There are two views here in my opinion.
- We simplify and remove the builders as they are not needed at all at this point, no need to support them.
- We keep the builders as they are with the thinking that if in the future will appear optional dependencies they will be introduced within the builder as setters and our refactoring will be minimal.
UNRELATED SIDE NOTE: In some rare cases where we have a very complex building logic that requires some things to be set before others or when setting something then we must proceed with additional settings for what we just set, then I like to do interactive builders where I have multiple interfaces with specific setters and my builder implements all giving me the possibility to guide the caller through the possible options at any given step, returning a specific interface. (not applicable at all to us, just an interesting side note)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the well reasoned reply. Here are my thoughts.
- Removing the builders and using a static
of
method makes sense in these small-value cases where you want all the required values set. My comment was that a constructor, specifically, should not have parameters; a static method is a secondary pattern (builder-factory-method), and it is fine to have parameters (in fact practically required) there.- It also makes sense to have a builder that is properly designed, internal to the class to build, and accessible only as a return value from an
of
method that takes the required parameters; this also avoids the perceived need to have a constructor with parameters while retaining regular "builder" semantics.
- It also makes sense to have a builder that is properly designed, internal to the class to build, and accessible only as a return value from an
- Re: the side note, I am not a huge fan of interactive builders with multiple interfaces (I've seen that become a nest of bugs that breeds more too many times). If there is an ordering or containment requirement, the intent of a builder would, in my opinion, be to remove the ordering/containment requirement and allow values to be set in any order, but then apply them to the created object in the correct order and containment during the
build
method. Abuild
method might, in this setup, construct an entire tree of objects in order to meet all the requirements for a particular highly complex object build.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jsync-swirlds awesome! Thanks also for your insights! I like the combination of the of
method that will get us an internal builder. I will implement that, it will clean up a lot of things! Just so that there is no confusion on my side:
- Do you propose that I will now remove the external builders and have a static
of
method that will be part of the target class, from where we will supply all mandatory dependencies AND since there are currently no cases (or will not be once I remove thefolderPermissions
) of optional dependencies, theof
method alone is sufficient. Once we have an optional dependency for the given class, theof
method will then return a newly made internal to the target class "Joshua Bloch" style builder and it will be an easy refactor.
OR
- Same as above, I remove the external builders, have an
of
method in the target class, but proactively create the internal builders and simply call thebuild
method where I want an instance of the target class, despite the fact that currently we have no optional deps.
?
In any case, I am generally pro
for static instance creation methods.
Let me know, thanks again, insightful thread! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would recommend starting with option (1), and we can always adjust later if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Done. Quite the refactoring it was, took a while, but everything is a lot cleaner :). One note, for the places where are no input params, I have used the method name 'newInstnace', the name 'of' and then passing nothing really did not fit at all. Since now everything is wired, a simple rename of all usages of 'newInstance' is trivial and takes milliseconds.
server/src/main/java/com/hedera/block/server/persistence/storage/write/BlockWriter.java
Outdated
Show resolved
Hide resolved
@jsync-swirlds thanks for the review, I have missed a couple of places where I could clean up additionally, however at some places it looks like you were looking at older changes, files were amended before you have made your review. I have also included #319 here and will bring additional changes to the exception handling to clean that up as well. |
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
…rsistence Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
…ethods Signed-off-by: Atanas Atanasov <[email protected]>
… methods Signed-off-by: Atanas Atanasov <[email protected]>
… via static methods Signed-off-by: Atanas Atanasov <[email protected]>
… via static methods Signed-off-by: Atanas Atanasov <[email protected]>
… methods Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Signed-off-by: Atanas Atanasov <[email protected]>
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #355 +/- ##
============================================
- Coverage 97.77% 94.02% -3.75%
- Complexity 343 358 +15
============================================
Files 70 76 +6
Lines 1256 1340 +84
Branches 88 91 +3
============================================
+ Hits 1228 1260 +32
- Misses 19 69 +50
- Partials 9 11 +2
|
Description:
This PR aims to introduce the new
BlockAsFileWriter
with the initial goal to only be able to write blocks as file. In subsequent PRs this functionality will be improved and cleaned up. You can see #309 for more details on the full functionality that will be implemented.Related issue(s):
Fixes #281 #319 #348
Notes for reviewer:
TBD
Checklist
repairPermissionsForRetry
#348